package com.example.gsp.proxy;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class Proxy {

    public static Object newProxyInstance(Class<?> inter, InvocationHandler h) throws Exception {
        String methodStr = "";
        for (Method method : inter.getDeclaredMethods()) {
            String paramStr = "";
            int i = 1;
            String paramClassStr = "";
            String paramObjs = "new Object[]{";
            for (Class<?> param : method.getParameterTypes()) {
                paramStr += param.getName() + " " + "param" + i + ",";
                paramClassStr += "," + param.getName() + ".class";
                paramObjs += "param" + i;
                i++;
            }
            paramObjs += "}";
            if (paramStr.length() > 0)
                paramStr = paramStr.substring(0, paramStr.length() - 1);

            methodStr +=
                    "    public " + method.getReturnType().getSimpleName() + " " + method.getName() + "(" + paramStr + ") {\n" +
                            "        try {\n" +
                            "            Method md = " + inter.getName() + ".class.getMethod(\"" + method.getName() + "\"" + paramClassStr + ");\n" +
                            "            h.invoke(this,md,"+paramObjs+");\n" +
                            "        } catch (Throwable e) {\n" +
                            "            e.printStackTrace();\n" +
                            "        }\n" +
                            "    }\n";
        }

        String str = "package com.example.gsp.proxy;\n" +
                "import java.lang.reflect.Method;\n" +
                "public class $Proxy0 implements " + inter.getName() + "{\n" +
                "    InvocationHandler h;\n" +
                "\n" +
                "    public $Proxy0( InvocationHandler h) {\n" +
                "        this.h = h;\n" +
                "    }\n" +
                "\n" +
                methodStr +
                "}";
        String fileName = System.getProperty("user.dir") +
                "/src/main/java/com/example/gsp/proxy/$Proxy0.java";
        File file = new File(fileName);
        FileWriter fw = new FileWriter(file);
        fw.write(str);
        fw.flush();
        fw.close();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> iterable = fileManager.getJavaFileObjects(fileName);
        JavaCompiler.CompilationTask compilationTask = compiler.getTask(null, fileManager, null, null, null, iterable);
        compilationTask.call();
        fileManager.flush();
        fileManager.close();

        //load class
        URL[] urls = new URL[]{new URL("file:/" + System.getProperty("user.dir") + "/src")};
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class claz = urlClassLoader.loadClass("com.example.gsp.proxy.$Proxy0");
        Constructor constructor = claz.getConstructor(InvocationHandler.class);
        Object o = constructor.newInstance(h);
        return o;
    }

}
